﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace YServer
{
    public class Y_Server
    {
        /// <summary>
        /// 最大连接数
        /// </summary>
        public int maxConn;

        Semaphore mConnSe;

        /// <summary>
        /// 最大挂起的连接数
        /// </summary>
        public int maxWait;

        /// <summary>
        /// 服务器端口
        /// </summary>
        public int port;

        /// <summary>
        /// 用于监听的连接套接字
        /// </summary>
        Socket listenSocket;

        /// <summary>
        /// 与异步操作套接字相关联的对象池（自定义）
        /// </summary>
        public ObjectPool<YToken> tokens;

        /// <summary>
        /// 消息处理中心
        /// </summary>
        public AbsHandlerCenter center;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="port">端口</param>
        /// <param name="maxConn">最大连接数</param>
        /// <param name="maxWait">最大挂起数</param>
        public Y_Server(int port,int maxConn,int maxWait=10) {
            this.port = port;
            this.maxConn = maxConn;
            this.maxWait = maxWait;
        }


        public void Start() {
            Init();

            if (center == null) {
                Console.WriteLine("请传入一个消息处理中心");
                return;
            }

            StartAccpet(null);
            Console.WriteLine("开始等待连接");
        }

        /// <summary>
        /// 初始化异步操作池 及绑定端口
        /// </summary>
        void Init() {

            tokens = new ObjectPool<YToken>(maxConn);

            for (int i = 0; i < maxConn; i++) {
                YToken token = new YToken();

                token.LD = LengthDEncode.LengthDe;
                token.LE = LengthDEncode.LengthEn;

                token.SD = SocketModelDEncode.SocketModelDe;
                token.SE = SocketModelDEncode.SocketModelEn;

                token.r_SAEArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                token.w_SAEArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);

                token.center = center;

                token.sendProcess = ProcessSend;
                token.closeProcess = CloseClientSocket;

                tokens.Push(token);
            }

            mConnSe = new Semaphore(maxConn, maxConn);

            try
            {
                listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                listenSocket.Bind(new IPEndPoint(IPAddress.Any, port));
                listenSocket.Listen(maxWait);
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
            }

        }


        /// <summary>
        /// 开始接收
        /// </summary>
        void StartAccpet(SocketAsyncEventArgs e) {
            //为空即为新连接，新建一个 e 对象，绑定接收异步操作，等待新的连接
            if (e == null)
            {
                e = new SocketAsyncEventArgs();
                e.Completed += new EventHandler<SocketAsyncEventArgs>(Accept_Completed);
            }
            else { //否则，把 e 中的AcceptSocket置空，等待一个新的连接
                e.AcceptSocket = null;
            }

            //信号量mConnSe减一
            mConnSe.WaitOne();

            // 返回结果:
            //     如果 I/O 操作挂起，将返回 true。 操作完成时，将引发 e 参数的 System.Net.Sockets.SocketAsyncEventArgs.Completed
            //     事件。 如果 I/O 操作同步完成，将返回 false。 将不会引发 e 参数的 System.Net.Sockets.SocketAsyncEventArgs.Completed
            //     事件，并且可能在方法调用返回后立即检查作为参数传递的 e 对象以检索操作的结果。
            //
            try
            {
                bool result = listenSocket.AcceptAsync(e);
                if (!result)
                {
                    ProcessAccept(e);
                }
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
            }

        }

        //接收异步操作的完成事件
        void Accept_Completed(object sender, SocketAsyncEventArgs e) {
            ProcessAccept(e);
        }

        //接收连接
        void ProcessAccept(SocketAsyncEventArgs e) {
            //从池中取出我们自定义的token对象 (此处为YToken)
            YToken token = tokens.Pop();
            token.socket = e.AcceptSocket;

            //TODO 通知上层应用有新的连接
            center.OnClientConnect(token);


            //尝试接收数据
            StartReceive(token);


            //继续接收新连接 (循环)
            StartAccpet(e);
        }

        /// <summary>
        /// 尝试接收数据
        /// </summary>
        /// <param name="token"></param>
        private void StartReceive(YToken token)
        {
            try
            {
                bool result = token.socket.ReceiveAsync(token.r_SAEArgs);
                // 返回结果:
                //     如果 I/O 操作挂起，将返回 true。 操作完成时，将引发 e 参数的 System.Net.Sockets.SocketAsyncEventArgs.Completed
                //     事件。 如果 I/O 操作同步完成，将返回 false。 将不会引发 e 参数的 System.Net.Sockets.SocketAsyncEventArgs.Completed
                //     事件，并且可能在方法调用返回后立即检查作为参数传递的 e 对象以检索操作的结果.
                if (!result)
                {
                    ProcessReceive(token.r_SAEArgs);
                }
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
            }
        }

        void IO_Completed(object sender, SocketAsyncEventArgs e) {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    ProcessSend(e);
                    break;
                default:
                    break;
            }
        }

        void ProcessReceive(SocketAsyncEventArgs e) {
            YToken token = (YToken)e.UserToken;
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                //e.SetBuffer(e.Offset, e.BytesTransferred);

                //token.Receive(e.Buffer);  这样写会把整个缓冲区的字节数都写入

                byte[] data = new byte[e.BytesTransferred];
                Buffer.BlockCopy(e.Buffer, 0, data, 0, data.Length);
                token.Receive(data);

                StartReceive(token);
            }
            else
            {
                CloseClientSocket(token,"客户端断开连接");
            }
        }

        void ProcessSend(SocketAsyncEventArgs e){
            YToken token = (YToken)e.UserToken;
            if (e.SocketError != SocketError.Success)
            {
                CloseClientSocket(token, e.SocketError.ToString());
            }
            else
            {
                //发送消息成功 继续发送
                token.Write();
            }
        }

        void CloseClientSocket(YToken token,string error) {
            if (token != null) {
                lock (token)
                {
                    //TODO 通知上层，客户端断开连接
                    center.OnClientClose(token, error);

                    token.Close();
                    tokens.Push(token);
                    mConnSe.Release();
                }

            }


        }


    }
}
